Iflow

Librerías

library(leaflet)
library(tidyverse)
library(readxl)
library(lubridate)
library(kableExtra)
library(GGally)
library(corrplot)
library(ggplot2)
library(dplyr)
library(gridExtra)
library(osmdata)
library(sf)
library(ggmap)
library(MASS)

Cargar Datos

df <- read_excel("iFlowDatos.xlsx") 
df
# A tibble: 27,484 × 16
   iddomicilioorden direccion      localidad InicioHorario1 FinHorario1 latitud
              <dbl> <chr>          <chr>              <dbl>       <dbl>   <dbl>
 1            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 2            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 3            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 4            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 5            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 6            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 7            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 8            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 9            74958 M PEDRAZA 2370 CAPITAL              901        1401   -34.6
10            74958 M PEDRAZA 2370 CAPITAL              901        1401   -34.6
# ℹ 27,474 more rows
# ℹ 10 more variables: longitud <dbl>, cliente <dbl>, mes <dbl>, Bultos <dbl>,
#   Peso <dbl>, Unidades <dbl>, InicioVisitaPlanificado <chr>,
#   FinVisitaPlanificado <chr>, InicioVisitaReal <chr>, FinVisitaReal <chr>

Limpieza de los Datos

df_clean <- df %>% 
  filter(latitud<(-34),longitud<(-58)) %>% 
  mutate(inicio = parse_date_time(InicioVisitaReal, orders = c("ymd HMS", "ymd_HMS", "dmy HMS"), tz = Sys.timezone(), quiet = TRUE)) %>% na.omit()%>%
  mutate(cliente = as.factor(cliente))

names(df_clean) <- tolower(names(df_clean))
df_clean
# A tibble: 27,382 × 17
   iddomicilioorden direccion      localidad iniciohorario1 finhorario1 latitud
              <dbl> <chr>          <chr>              <dbl>       <dbl>   <dbl>
 1            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 2            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 3            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 4            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 5            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 6            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 7            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 8            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 9            74958 M PEDRAZA 2370 CAPITAL              901        1401   -34.6
10            74958 M PEDRAZA 2370 CAPITAL              901        1401   -34.6
# ℹ 27,372 more rows
# ℹ 11 more variables: longitud <dbl>, cliente <fct>, mes <dbl>, bultos <dbl>,
#   peso <dbl>, unidades <dbl>, iniciovisitaplanificado <chr>,
#   finvisitaplanificado <chr>, iniciovisitareal <chr>, finvisitareal <chr>,
#   inicio <dttm>
dim(df_clean)
[1] 27382    17

Análisis de Datos Básico

summary(df_clean)
 iddomicilioorden  direccion          localidad         iniciohorario1
 Min.   : 74956   Length:27382       Length:27382       Min.   :  0   
 1st Qu.: 77460   Class :character   Class :character   1st Qu.:901   
 Median : 82348   Mode  :character   Mode  :character   Median :901   
 Mean   :101622                                         Mean   :901   
 3rd Qu.:121664                                         3rd Qu.:901   
 Max.   :183277                                         Max.   :901   
  finhorario1      latitud          longitud      cliente         mes       
 Min.   :1400   Min.   :-34.85   Min.   :-58.73   20:16510   Min.   :5.000  
 1st Qu.:1401   1st Qu.:-34.62   1st Qu.:-58.48   70:10872   1st Qu.:5.000  
 Median :1401   Median :-34.60   Median :-58.44              Median :6.000  
 Mean   :1401   Mean   :-34.60   Mean   :-58.44              Mean   :6.021  
 3rd Qu.:1401   3rd Qu.:-34.58   3rd Qu.:-58.40              3rd Qu.:7.000  
 Max.   :2359   Max.   :-34.39   Max.   :-58.15              Max.   :7.000  
     bultos             peso            unidades       iniciovisitaplanificado
 Min.   :  0.100   Min.   :   0.00   Min.   :   1.00   Length:27382           
 1st Qu.:  2.000   1st Qu.:  13.00   1st Qu.:   2.00   Class :character       
 Median :  3.000   Median :  20.94   Median :   6.00   Mode  :character       
 Mean   :  5.692   Mean   :  40.95   Mean   :  28.37                          
 3rd Qu.:  6.000   3rd Qu.:  39.00   3rd Qu.:  40.00                          
 Max.   :360.000   Max.   :2475.00   Max.   :2203.00                          
 finvisitaplanificado iniciovisitareal   finvisitareal     
 Length:27382         Length:27382       Length:27382      
 Class :character     Class :character   Class :character  
 Mode  :character     Mode  :character   Mode  :character  
                                                           
                                                           
                                                           
     inicio                      
 Min.   :2024-05-03 07:17:51.00  
 1st Qu.:2024-05-24 15:38:00.00  
 Median :2024-06-18 10:34:53.00  
 Mean   :2024-06-17 22:15:43.75  
 3rd Qu.:2024-07-11 11:21:32.00  
 Max.   :2024-08-06 16:57:00.00  

Distintas Variables

colnames(df_clean)
 [1] "iddomicilioorden"        "direccion"              
 [3] "localidad"               "iniciohorario1"         
 [5] "finhorario1"             "latitud"                
 [7] "longitud"                "cliente"                
 [9] "mes"                     "bultos"                 
[11] "peso"                    "unidades"               
[13] "iniciovisitaplanificado" "finvisitaplanificado"   
[15] "iniciovisitareal"        "finvisitareal"          
[17] "inicio"                 
sapply(df_clean, class)
$iddomicilioorden
[1] "numeric"

$direccion
[1] "character"

$localidad
[1] "character"

$iniciohorario1
[1] "numeric"

$finhorario1
[1] "numeric"

$latitud
[1] "numeric"

$longitud
[1] "numeric"

$cliente
[1] "factor"

$mes
[1] "numeric"

$bultos
[1] "numeric"

$peso
[1] "numeric"

$unidades
[1] "numeric"

$iniciovisitaplanificado
[1] "character"

$finvisitaplanificado
[1] "character"

$iniciovisitareal
[1] "character"

$finvisitareal
[1] "character"

$inicio
[1] "POSIXct" "POSIXt" 

Cambiar las variables, para que las que tengan que estar en formato fecha, lo estén

df_clean$iniciovisitaplanificado <- as.POSIXct(df_clean$iniciovisitaplanificado, format = "%Y-%m-%d %H:%M:%OS", tz = "UTC")
df_clean$finvisitaplanificado <- as.POSIXct(df_clean$finvisitaplanificado, format = "%Y-%m-%d %H:%M:%OS", tz = "UTC")
df_clean$iniciovisitareal <- as.POSIXct(df_clean$iniciovisitareal, format = "%Y-%m-%d %H:%M:%OS", tz = "UTC")
df_clean$finvisitareal <- as.POSIXct(df_clean$finvisitareal, format = "%Y-%m-%d %H:%M:%OS", tz = "UTC")

df_clean
# A tibble: 27,382 × 17
   iddomicilioorden direccion      localidad iniciohorario1 finhorario1 latitud
              <dbl> <chr>          <chr>              <dbl>       <dbl>   <dbl>
 1            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 2            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 3            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 4            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 5            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 6            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 7            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 8            74956 VIDAL 2044     CAPITAL              901        1401   -34.6
 9            74958 M PEDRAZA 2370 CAPITAL              901        1401   -34.6
10            74958 M PEDRAZA 2370 CAPITAL              901        1401   -34.6
# ℹ 27,372 more rows
# ℹ 11 more variables: longitud <dbl>, cliente <fct>, mes <dbl>, bultos <dbl>,
#   peso <dbl>, unidades <dbl>, iniciovisitaplanificado <dttm>,
#   finvisitaplanificado <dttm>, iniciovisitareal <dttm>, finvisitareal <dttm>,
#   inicio <dttm>
colnames(df_clean)
 [1] "iddomicilioorden"        "direccion"              
 [3] "localidad"               "iniciohorario1"         
 [5] "finhorario1"             "latitud"                
 [7] "longitud"                "cliente"                
 [9] "mes"                     "bultos"                 
[11] "peso"                    "unidades"               
[13] "iniciovisitaplanificado" "finvisitaplanificado"   
[15] "iniciovisitareal"        "finvisitareal"          
[17] "inicio"                 
sapply(df_clean, class)
$iddomicilioorden
[1] "numeric"

$direccion
[1] "character"

$localidad
[1] "character"

$iniciohorario1
[1] "numeric"

$finhorario1
[1] "numeric"

$latitud
[1] "numeric"

$longitud
[1] "numeric"

$cliente
[1] "factor"

$mes
[1] "numeric"

$bultos
[1] "numeric"

$peso
[1] "numeric"

$unidades
[1] "numeric"

$iniciovisitaplanificado
[1] "POSIXct" "POSIXt" 

$finvisitaplanificado
[1] "POSIXct" "POSIXt" 

$iniciovisitareal
[1] "POSIXct" "POSIXt" 

$finvisitareal
[1] "POSIXct" "POSIXt" 

$inicio
[1] "POSIXct" "POSIXt" 

Análisis

df_clean <- df_clean %>%
  mutate(TiempoEntrega = finvisitareal - iniciovisitareal)

Cantidad de entregas por localidad

df_counts <- df_clean %>%
  group_by(localidad) %>%
  summarise(conteo = n())%>%
  arrange(desc(conteo))%>%
  head(5)

ggplot(df_counts, aes(x = localidad, y =conteo)) +
  geom_bar(stat = "identity") 

Este gráfico permite identificar las 5 localidades con mayor carga de entregas y lo hicimos para explorar el dato de localidad. Las localidades con barras más altas son las que tienen un mayor volumen de entregas, lo cual puede ayudar a la empresa a focalizar recursos en estas áreas.

Calculamos el tiempo promedio de demora en la salida, y en la llegada (Los datos de las visitas planificadas están mal cargados, por lo que este análisis no lo pudimos hacer de forma correcta)

calcular_tiempo_formateado <- function(inicio, fin) {
  # Calcula la diferencia en minutos con decimales
  tiempo_minutos_dec <- as.numeric(difftime(fin, inicio, units = "mins"))
 
  tiempo_minutos <- floor(tiempo_minutos_dec)
  # Retorna el valor numérico
  return(tiempo_minutos)
}

df_clean <- df_clean %>%
  mutate(DemoraSalida = calcular_tiempo_formateado(iniciovisitaplanificado, iniciovisitareal))%>%
  mutate(DemoraLlegada = calcular_tiempo_formateado(finvisitaplanificado, finvisitareal))

promedio_demora_salida <- mean(df_clean$DemoraSalida,na.rm = TRUE)
promedio_demora_salida
[1] 133.482
promedio_demora_llegada <- mean(df_clean$DemoraLlegada,na.rm=TRUE)
promedio_demora_llegada
[1] 139.4571

Calculamos el tiempo promedio de demora en la salida y llegada.

Con lo calculado arriba, sacamos el tiempo promedio de demora en la salida y llegada, en las distintas localidades y graficamos

df_demora_por_localidad <- df_clean%>%
  group_by(localidad) %>%
  summarise(demora_salida_localidad=mean(DemoraSalida,na.rm = T),demora_llegada_localidad=mean(DemoraLlegada,na.rm = T)) %>%
  arrange(demora_salida_localidad) %>%
  filter(demora_salida_localidad < 3000) %>%
  mutate(localidad = factor(localidad, levels = localidad))
  
  
ggplot(df_demora_por_localidad,aes(x=localidad,y=demora_salida_localidad)) +
  geom_bar(stat="identity") +
  labs(title = "Demora de Salida por Localidad",
       x = "Localidad",
       y = "Demora de Salida (promedio)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(df_demora_por_localidad,aes(x=localidad,y=demora_llegada_localidad))+
  geom_bar(stat="identity")+
  labs(title = "Demora de llegada por Localidad",
       x = "Localidad",
       y = "Demora de Llegada (promedio)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Ahora calculamos la demora promedio por cliente

demora_promedio_por_cliente <- df_clean%>%
  group_by(cliente) %>%
  summarise(demora_salida_cliente=mean(DemoraSalida,na.rm=T),demora_llegada_cliente=mean(DemoraLlegada,na.rm=T))

demora_melt <- reshape2::melt(demora_promedio_por_cliente, id.vars = 'cliente')

# Crear el gráfico con ggplot2
ggplot(demora_melt, aes(x = factor(cliente), y = value, fill = variable)) + 
  geom_bar(stat = 'identity', position = 'dodge') + 
  labs(x = 'Cliente', y = 'Demora', fill = 'Tipo de demora') + 
  scale_fill_manual(
    values = c("demora_salida_cliente" = "#FF6347", "demora_llegada_cliente" = "#20B2AA"),
    labels = c("demora_salida_cliente" = "Demora de Salida por Cliente", 
               "demora_llegada_cliente" = "Demora de Llegada por Cliente")
  ) + 
  theme_minimal()

Distribución de las entregas en un mapa

library(dplyr)
library(sf)
library(plotly)

Attaching package: 'plotly'
The following object is masked from 'package:MASS':

    select
The following object is masked from 'package:ggmap':

    wind
The following object is masked from 'package:ggplot2':

    last_plot
The following object is masked from 'package:stats':

    filter
The following object is masked from 'package:graphics':

    layout
Sys.setenv("MAPBOX_TOKEN" = "pk.eyJ1IjoiYWd1c3Rpbm9ydWUiLCJhIjoiY20yamR1dmlhMDRnMjJscHgxZTk4a3Z1OSJ9.3XpQh1Kfpitza34209j9vA")

df.sf <- df %>%
  filter(!is.na(longitud) & !is.na(latitud)) %>%
  filter(longitud < 0) %>%
  filter(latitud < -34.0) %>%
  st_as_sf(coords = c("longitud", "latitud"), crs = 4326)

coords <- st_coordinates(df.sf)

plot_ly() %>%
  add_trace(
    type = 'scattermapbox',
    mode = 'markers',
    lon = coords[, 1],
    lat = coords[, 2],  
    marker = list(size = 8, color = 'blue')  
  ) %>%
  layout(
    mapbox = list(
      style = 'streets', 
      zoom = 8,  
      center = list(lon = mean(coords[, 1]), lat = mean(coords[, 2]))  
    ),
    showlegend = FALSE  
  ) %>%
  config(mapboxAccessToken = Sys.getenv("MAPBOX_TOKEN")) 

Concentración de entregas

library(leaflet.extras)

leaflet(df_clean) %>%
  addTiles() %>%
  addHeatmap(lng = ~longitud, lat = ~latitud, intensity = ~unidades, blur = 25, max = 0.05, radius = 15) %>%
  addLegend(position = "bottomright", colors = "red", labels = "Concentración de envíos", title = "Leyenda")

Estos mapas muestran dónde se encuentran las entregas en el área de interés, lo cual es útil para identificar zonas con alta o baja concentración de entregas. Puede ayudar a identificar si las entregas están concentradas en ciertas áreas o si están distribuidas uniformemente.

Distribucion de las entregas de los distintos clientes

library(dplyr)
library(sf)
library(plotly)

Sys.setenv("MAPBOX_TOKEN" = "pk.eyJ1IjoiYWd1c3Rpbm9ydWUiLCJhIjoiY20yamR1dmlhMDRnMjJscHgxZTk4a3Z1OSJ9.3XpQh1Kfpitza34209j9vA")

df.sf <- df %>%
  filter(!is.na(longitud) & !is.na(latitud)) %>%
  filter(longitud < 0) %>%
  filter(latitud < -34.0) %>%
  st_as_sf(coords = c("longitud", "latitud"), crs = 4326)

# Extraer las coordenadas
coords <- st_coordinates(df.sf)

# Asignar etiquetas para la leyenda
df.sf$label <- case_when(
  df.sf$cliente == 20 ~ 'Cliente 20',  # Etiqueta para cliente 20
  df.sf$cliente == 70 ~ 'Cliente 70',  # Etiqueta para cliente 70
  TRUE ~ 'Otros Clientes'  # Otros clientes
)

# Asignar colores basados en la etiqueta
df.sf$color <- case_when(
  df.sf$cliente == 20 ~ '#FF6347',  
  df.sf$cliente == 70 ~ '#20B2AA',  
  TRUE ~ 'black'  
)

# Crear el gráfico con leyenda y colores personalizados
plot_ly() %>%
  add_trace(
    type = 'scattermapbox',
    mode = 'markers',
    lon = coords[, 1],  
    lat = coords[, 2],  
    text = df.sf$cliente,  
    marker = list(
      size = 8,
      color = df.sf$color  
    ),
    showlegend = TRUE,
    name = df.sf$label  
  ) %>%
  layout(
    mapbox = list(
      style = 'streets',
      zoom = 8,  
      center = list(lon = mean(coords[, 1]), lat = mean(coords[, 2]))  
    ),
    showlegend = TRUE  
  ) %>%
  config(mapboxAccessToken = Sys.getenv("MAPBOX_TOKEN"))

Concentración de las entregas de los distintos clientes

library(leaflet)
library(leaflet.extras) 
library(dplyr)
library(KernSmooth)
KernSmooth 2.23 loaded
Copyright M. P. Wand 1997-2009
library(readxl)

df_clean <- df_clean %>%
  mutate(
    lat = as.numeric(df_clean$latitud),
    lon = as.numeric(df_clean$longitud)
  ) %>%
  filter(!is.na(latitud) & !is.na(longitud))  # Filtrar para eliminar filas con NA

df_cliente_20 <- df_clean %>% filter(cliente == 20)  
df_cliente_70 <- df_clean %>% filter(cliente == 70)  

# Crear el mapa interactivo
leaflet() %>%
  addTiles() %>%

  addHeatmap(
    data = df_cliente_20, 
    lng = ~lon, lat = ~lat, intensity = 1,
    blur = 28, max = 0.1, radius = 15,
    gradient = colorRampPalette(c("transparent", "blue", "yellow", "red"))(256),
    group = "Cliente 20"  
  ) %>%

  addHeatmap(
    data = df_cliente_70, 
    lng = ~lon, lat = ~lat, intensity = 1,
    blur = 20, max = 0.1, radius = 15,
    gradient = colorRampPalette(c("transparent", "green", "yellow", "orange"))(256),
    group = "Cliente 70"  
  ) %>%
  setView(lng = mean(df_clean$longitud, na.rm = TRUE), lat = mean(df_clean$latitud, na.rm = TRUE), zoom = 10) %>%
  addLayersControl(
    overlayGroups = c("Cliente 20", "Cliente 70"),
    options = layersControlOptions(collapsed = FALSE)  
  ) %>%
  addLegend(position = "bottomright", 
            colors = colorRampPalette(c("blue", "yellow", "red"))(3), 
            labels = c("Baja concentración", "Media concentración", "Alta concentración"), 
            title = "Densidad Cliente 20") %>%
  addLegend(position = "bottomleft", 
            colors = colorRampPalette(c("green", "yellow", "orange"))(3), 
            labels = c("Baja concentración", "Media concentración", "Alta concentración"), 
            title = "Densidad Cliente 70")
library(leaflet)
library(leaflet.extras)
library(dplyr)
library(readxl)

df_clean <- df_clean %>%
  mutate(
    lat = as.numeric(latitud),
    lon = as.numeric(longitud)
  ) %>%
  filter(!is.na(lat) & !is.na(lon))


df_cliente_20 <- df_clean %>% filter(cliente == 20)
df_cliente_70 <- df_clean %>% filter(cliente == 70)


leaflet() %>%
  addTiles() %>%
  # Añadir markers para cliente 20 con clustering
  addMarkers(
    data = df_cliente_20,
    lng = ~lon, lat = ~lat,
    clusterOptions = markerClusterOptions(),
    group = "Cliente 20"
  ) %>%
  # Añadir markers para cliente 70 con clustering
  addMarkers(
    data = df_cliente_70,
    lng = ~lon, lat = ~lat,
    clusterOptions = markerClusterOptions(),
    group = "Cliente 70"
  ) %>%
  setView(lng = mean(df_clean$lon, na.rm = TRUE), lat = mean(df_clean$lat, na.rm = TRUE), zoom = 10) %>%
  addLayersControl(
    overlayGroups = c("Cliente 20", "Cliente 70"),
    options = layersControlOptions(collapsed = FALSE)
  ) 

Estos mapas muestran la ubicación de las entregas y permite identificar patrones geográficos específicos para los clientes destacados. Esto puede ayudar a entender las aréas en las cuales los clientes están mayormente concentrados.

La diferenciación de clientes a través de colores y etiquetas facilita la visualización y el análisis. En estos gráficos, también se puede seleccionar la opción de visualizar los 2 clientes al mismo tiempo, ó cada uno por separado.

Peso por unidad

peso_por_unidad <- function(peso, unidades) {
  if (unidades == 0) {
    return(NA)  
  } else {
    return(peso / unidades) 
  }
}

df$peso_por_unidad <- mapply(peso_por_unidad, df$Peso, df$Unidades)

head(df[c("Peso", "Unidades", "peso_por_unidad")])
# A tibble: 6 × 3
   Peso Unidades peso_por_unidad
  <dbl>    <dbl>           <dbl>
1  24.9       30           0.830
2  26.3       38           0.691
3  24.9       32           0.778
4  14.0       28           0.501
5  14.4       31           0.464
6  13.8       25           0.551
ggplot(df, aes(x = peso_por_unidad)) +
  geom_histogram(binwidth = 0.6, fill = "blue", color = "black") +
  labs(title = "Distribución del Peso por Unidad", x = "Peso por Unidad", y = "Frecuencia")

Este gráfico muestra la cantidad de unidades que hay por peso.

Cantidad de entregas por mes

# Cargar librerías necesarias
library(ggplot2)
library(lubridate)

df$InicioVisitaReal <- as.Date(df$InicioVisitaReal, format = "%Y-%m-%d")

df_cantidad_de_entregas_por_dia<-df%>%
  group_by(InicioVisitaReal)%>%
  summarise(n=n())%>%
  mutate(mes = month(InicioVisitaReal))

ggplot(df_cantidad_de_entregas_por_dia, aes(x = InicioVisitaReal, y = n)) +
  geom_line(size = 0.8, color="blue") +  
  geom_point(size = 1.5, color="blue") + 
  labs(title = "Cantidad de entregas a lo largo del tiempo por mes", 
       x = "Fecha de Inicio de Visita", 
       y = "Cantidad de entregas") +
  theme_minimal() +
  theme(panel.grid.major = element_line(color = "gray90"))  
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).

El gráfico de líneas muestra cómo varía el número de entregas día a día, lo cual puede ayudar a identificar patrones estacionales. Se puede observar si hay picos en ciertas fechas, lo que podría coincidir con eventos o períodos de alta demanda.

Cantidad de entregas por cliente en todos los meses

df$InicioVisitaPlanificado <- as.Date(df$InicioVisitaPlanificado, format = "%Y-%m-%d")
df$cliente<-as.character(df$cliente)
cantidad_entregas_por_cliente<-df%>%
  group_by(InicioVisitaPlanificado,cliente)%>%
  summarise(n=n())%>%
  mutate(mes = month(InicioVisitaPlanificado))
cantidad_entregas_por_cliente
# A tibble: 149 × 4
# Groups:   InicioVisitaPlanificado [77]
   InicioVisitaPlanificado cliente     n   mes
   <date>                  <chr>   <int> <dbl>
 1 2024-05-03              20        192     5
 2 2024-05-03              70        159     5
 3 2024-05-04              20        212     5
 4 2024-05-04              70        141     5
 5 2024-05-06              20        175     5
 6 2024-05-06              70        141     5
 7 2024-05-07              20        292     5
 8 2024-05-07              70        184     5
 9 2024-05-08              20        321     5
10 2024-05-08              70        164     5
# ℹ 139 more rows
ggplot(cantidad_entregas_por_cliente, aes(x =InicioVisitaPlanificado , y = n, color = cliente)) +
  geom_line(size = 1) +  
  geom_point(size = 2.5) +  
  labs(title = "Cantidad de productos entregados por cliente cada día", 
       x = "Fecha de Entrega", 
       y = "Cantidad de Productos Entregados",
       color = "Cliente") +  
  theme_minimal()

Cada línea representa la tendencia de entregas de un cliente en el tiempo. Esto permite comparar el comportamiento de cada cliente y ver quién tiene más o menos entregas en un periodo determinado.

Distribución de Entregas en las distintas horas del día

# Cargar las librerías necesarias
library(ggplot2)
library(dplyr)

# Convertir 'InicioVisitaReal' a formato POSIXct
df_clean$iniciovisitareal <- as.POSIXct(df_clean$iniciovisitareal, format = "%Y-%m-%d %H:%M:%S")

# Crear las columnas 'dia' y 'hora' a partir de 'InicioVisitaReal'
df_clean$dia <- format(df_clean$iniciovisitareal, "%Y-%m-%d")
df_clean$hora <- format(df_clean$iniciovisitareal, "%H")

# Agrupar los datos por día y contar el número de entregas por día
entregas_por_dia <- df_clean %>%
  group_by(dia) %>%
  summarise(cantidad_entregas = n())

# Agrupar los datos por hora y contar el número de entregas por hora
# Convertir 'hora' a numérico para el eje X
df_clean$hora <- as.numeric(df_clean$hora)

entregas_por_hora <- df_clean %>%
  group_by(hora) %>%
  summarise(cantidad_entregas = n())

# Graficar la cantidad de entregas por hora
ggplot(entregas_por_hora, aes(x = hora, y = cantidad_entregas)) +
  geom_bar(stat = "identity", fill = "lightblue", color = "black") +
  labs(title = "Cantidad de Entregas por Hora", x = "Hora", y = "Cantidad de Entregas") +
  theme_minimal() +
  scale_x_continuous(breaks = 0:23)  # Asegurarse de que el eje X muestra las horas adecuadamente

Podemos observar la cantidad de entregas en las distintas horas del día, lo que nos puede ayudar a ver en que horarios hay una mayor demanda de entregas.

Distribución de entregas en los distintos días de la semana

library(plotly)
library(dplyr)
library(sf)
library(lubridate)

df$InicioVisitaPlanificado <- as.Date(df$InicioVisitaPlanificado, format = "%Y-%m-%d")

df <- df %>%
  mutate(dia_semana = wday(InicioVisitaPlanificado, label = TRUE, abbr = FALSE))

df.sf <- df %>%
  filter(!is.na(longitud) & !is.na(latitud)) %>%
  filter(longitud < 0) %>%
  filter(latitud < -34.0) %>%
  st_as_sf(coords = c("longitud", "latitud"), crs = 4326)

coords <- st_coordinates(df.sf)

plot_ly() %>%
  add_trace(
    type = 'scattermapbox',
    mode = 'markers',
    lon = coords[, 1],
    lat = coords[, 2],  
    text = ~paste(df.sf$cliente, "<br>", df.sf$dia_semana), 
    marker = list(
      size = 8
    ),
    color = ~df.sf$dia_semana,  
    colors = 'Set1'  
  ) %>%
  layout(
    mapbox = list(
      style = 'streets',
      zoom = 8,  
      center = list(lon = mean(coords[, 1]), lat = mean(coords[, 2])) 
    ),
    showlegend = TRUE  
  ) %>%
  config(mapboxAccessToken = Sys.getenv("MAPBOX_TOKEN"))

En este mapa se puede ver la distribución de las entregas en los distintos días de la semana, viendo en que días se repartieron mayores entregas. Se pueden observar los datos de todos los días al mismo tiempo, ó cada día separado al tocar 2 veces en el día deseado.

Cluster de Entregas por Localidad

coords <- df_clean %>% 
  select(latitud, longitud) %>%
  na.omit()  


clusters <- kmeans(coords, centers = 3)

df_clean$cluster <- as.factor(clusters$cluster)


ggplot(df_clean, aes(x = longitud, y = latitud, color = cluster)) +  
  geom_point() +
  labs(title = "Clusters de Entregas por Localidad") +
  theme_minimal()

Cada color representa un grupo geográfico de entregas, lo que ayuda a identificar áreas con mayor densidad de actividad. Los clusters pueden sugerir la presencia de distintas zonas de entrega con alta demanda o regiones donde las entregas tienden a agruparse.

Distribución de la cantidad de unidades

library(leaflet)
library(leaflet.extras)

leaflet(df_clean) %>%
  addTiles() %>%
  addHeatmap(lng = ~longitud, lat = ~latitud, intensity = ~unidades, blur = 20, max = 0.05, radius = 15) %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud, radius = ~sqrt(unidades),
                   color = "#0074D9", fillColor = "#0074D9", fillOpacity = 0.5, 
                   popup = ~paste("Cantidad de Unidades:", unidades),
                   clusterOptions = markerClusterOptions()) 

Las áreas con mayor intensidad de color representan zonas con una alta cantidad de unidades entregadas.

Conclusión

En resumen, este análisis nos ha dado una visión completa de cómo se realizan las entregas en iFlow. Con esta información, iFlow puede hacer cambios que ayuden a mejorar la eficiencia del servicio, a organizar mejor los recursos y a ofrecer una mejor experiencia al cliente. Planear las entregas usando estos datos ayudará a que la empresa esté siempre lista para adaptarse y mejorar. También, el trabajo analiza la logística de transporte, con un enfoque en el rendimiento de tiempos y demoras asociados a distintas localidades, clientes y distancias recorridas. Se realizo un análisis espacial, mediante un mapa de eventos, que muestra la distribución geográfica de actividades, revelando áreas de alta concentración donde se puede enfocar mejor la logística de recursos.